package com.example.livedata.http;

import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MediatorLiveData;
import android.os.AsyncTask;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;

import com.example.livedata.api.ApiResponse;

// ResultType: Resource 数据的类型
// RequestType: API 响应的类型
public abstract class NetworkBoundResource<ResultType, RequestType> {

    private final MediatorLiveData<Resource<ResultType>> result = new MediatorLiveData<>();

    @MainThread
    public NetworkBoundResource() {
        result.setValue((Resource<ResultType>) Resource.loading(null));
        LiveData<ResultType> dbSource = loadFromDb();
        result.addSource(dbSource, data -> {
            result.removeSource(dbSource);
            if (shouldFetch(data)) {
                fetchFromNetwork(dbSource);
            } else {
                result.addSource(dbSource,
                        newData -> result.setValue(Resource.success(newData)));
            }
        });
    }

    private void fetchFromNetwork(final LiveData<ResultType> dbSource) {
        LiveData<ApiResponse<RequestType>> apiResponse = createCall();
        // 重新附加 dbSource 作为新的来源,
        // 它将会迅速发送最新的值。
        result.addSource(dbSource,
                newData -> result.setValue(Resource.loading(newData)));
        result.addSource(apiResponse, response -> {
            result.removeSource(apiResponse);
            result.removeSource(dbSource);
            //noinspection ConstantConditions
            if (response.isSuccessful()) {
                saveResultAndReInit(response);
            } else {
                onFetchFailed();
                result.addSource(dbSource,
                        newData -> result.setValue(
                                Resource.error(response.errorMessage, newData)));
            }
        });
    }

    @MainThread
    private void saveResultAndReInit(final ApiResponse<RequestType> response) {
        new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... voids) {
                saveCallResult(response.body);
                return null;
            }

            @Override
            protected void onPostExecute(Void aVoid) {
                // 指定请求一个最新的实时数据。
                // 否则，会得到最新的缓存数据，并且可能不会由从网络获取的最新数据更新。
                result.addSource(loadFromDb(),
                        newData -> result.setValue(Resource.success(newData)));
            }
        }.execute();
    }


    // 调用该方法将 API 响应的结果保存到数据库中。
    @WorkerThread
    protected abstract void saveCallResult(@NonNull RequestType item);

    // 调用该方法判断数据库中的数据是否应该从网络获取并更新。
    @MainThread
    protected abstract boolean shouldFetch(@Nullable ResultType data);

    // 调用该方法从数据库中获取缓存数据。
    @NonNull
    @MainThread
    protected abstract LiveData<ResultType> loadFromDb();

    // 调用该方法创建 API 请求。
    @NonNull
    @MainThread
    protected abstract LiveData<ApiResponse<RequestType>> createCall();

    // 获取失败时调用。
    // 子类可能需要充值组件（如：速率限制器）。
    @MainThread
    protected void onFetchFailed() {
    }

    // 返回一个代表 Resource 的 LiveData。
    public final LiveData<Resource<ResultType>> getAsLiveData() {
        return result;
    }
}